use askama::Template;
use axum::{
    extract::Query,
    http::StatusCode,
    response::{Html, IntoResponse, Redirect},
    routing::{get, post},
    Form, Router,
};
use axum_messages::{Message, Messages};
use serde::Deserialize;

use crate::users::{AuthSession, Credentials};

#[derive(Template)]
#[template(path = "login.html")]
pub struct LoginTemplate {
    messages: Vec<Message>,
    next: Option<String>,
    qrcode_base64: String,
    qr_url: String,
    hotspot_qr: String,
    hotspot_url: String,
}

// This allows us to extract the "next" field from the query string. We use this
// to redirect after log in.
#[derive(Debug, Deserialize)]
pub struct NextUrl {
    next: Option<String>,
}

pub fn router() -> Router<()> {
    Router::new()
        .route("/login", post(self::post::login))
        .route("/login", get(self::get::login))
        .route("/logout", get(self::get::logout))
        .route("/favicon.ico", get(self::get::favicon))
}

mod post {
    use base64::encode;

    use super::*;

    pub async fn login(
        mut auth_session: AuthSession,
        messages: Messages,
        Form(creds): Form<Credentials>,
    ) -> impl IntoResponse {
        println!("post login is called");
        let user = match auth_session.authenticate(creds.clone()).await {
            Ok(Some(user)) => user,
            Ok(None) => {
                messages.error("Invalid credentials");

                let mut login_url = "/login".to_string();
                if let Some(next) = creds.next {
                    // 关键：对 next 参数进行 URL 编码，避免特殊字符导致 URL 失效
                    login_url = format!("{}?next={}", login_url, encode(&next));
                };

                return Redirect::to(&login_url).into_response();
            }
            Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        };
        println!("login awaited (登录成功)");
        if let Err(e) = auth_session.login(&user).await {
            eprintln!("[Login Error] 登录失败（会话保存失败）: {}", e);
            return StatusCode::INTERNAL_SERVER_ERROR.into_response();
        }
        println!("[Login Success] 会话已保存，用户: {}", user.username);

        // 登录成功跳转：同样建议编码（可选，若 next 来自用户输入）
        let redirect_url = "/";
        Redirect::to(&redirect_url).into_response()
    }

}

mod get {
    use axum_login::AuthManager;
    use crate::users::Backend;

    use super::*;

    // src/web/auth/get.rs 的 login 函数
    pub async fn login(
        messages: Messages,
        Query(NextUrl { next }): Query<NextUrl>,
        mut auth_session: AuthSession
    ) -> impl IntoResponse { // 注意：返回类型改为 impl IntoResponse，支持 Redirect
        // 关键：已登录用户直接跳转到目标页（避免停留在登录页）
        if auth_session.user.is_some() {
            let redirect_url = next.as_deref().unwrap_or("/");
            println!("[GET Login] 已登录，跳转到: {}", redirect_url);
            return Redirect::to(redirect_url).into_response();
        }

        // 未登录：正常渲染登录页（原逻辑）
        let qrcode_base64 = crate::util::generate_qr_from_str(&crate::util::get_qr_url());
        let hotspot_qr = crate::util::generate_qr_from_str(&crate::util::get_hotspot_url());
        println!("get login is called (未登录)");
        Html(
            LoginTemplate {
                messages: messages.into_iter().collect(),
                next,
                qrcode_base64,
                qr_url: crate::util::get_qr_url(),
                hotspot_qr: hotspot_qr,
                hotspot_url: crate::util::get_hotspot_url(),
            }
            .render()
            .unwrap(),
        ).into_response()
    }

    pub async fn logout(mut auth_session: AuthSession) -> impl IntoResponse {
        match auth_session.logout().await {
            Ok(_) => Redirect::to("/login").into_response(),
            Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        }
    }

    // 处理 favicon.ico 请求
    pub async fn favicon() -> impl IntoResponse {
        let path = std::path::PathBuf::from("favicon.ico"); // 图标文件路径
        // 使用 tokio::fs 读取文件
        match tokio::fs::read(&path).await {
            Ok(content) => {
                (
                    StatusCode::OK,
                    [(axum::http::header::CONTENT_TYPE, "image/x-icon")],
                    content,
                ).into_response()
            }
            Err(_) => (StatusCode::NOT_FOUND, "Favicon not found").into_response(),
        }
    }
}
